package org.springframework.beans.factory.config;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.anno.Bean;
import org.springframework.beans.factory.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.util.ConfigurationClassUtils;
import org.springframework.beans.factory.util.StringUtils;
import org.springframework.core.logger.AbstractLogger;
import org.springframework.core.logger.LoggerFactory;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardMethodMetadata;

import java.util.*;

/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/12/14
 */
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private final Set<Integer> registriesPostProcessed = new HashSet<>();
    private final Set<Integer> factoriesPostProcessed = new HashSet<>();
    AbstractLogger logger = LoggerFactory.getLogger(ConfigurationClassPostProcessor.class);

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        int registryId = System.identityHashCode(registry);
        if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
        }
        if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + registry);
        }
        this.registriesPostProcessed.add(registryId);

        processConfigBeanDefinitions(registry);

    }

    private void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

        List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

        String[] candidateNames = registry.getBeanDefinitionNames();

        for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);

            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                    ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }

            else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef)) {
                configCandidates.add(new BeanDefinitionHolder(beanName,beanDef));
            }
        }
        if (configCandidates.isEmpty()) {
            return;
        }

        ConfigurationClassParser parser = new ConfigurationClassParser(registry);

        parser.parse(configCandidates);

        this.loadBeanDefinitions(registry,parser.getConfigurationClazzs());
    }

    private void loadBeanDefinitions(BeanDefinitionRegistry registry,List<ConfigurationClass> configurationClazzs) {
        for (ConfigurationClass config :configurationClazzs) {
            loadBeanDefinitions(registry,config);
        }
    }

    private void loadBeanDefinitions(BeanDefinitionRegistry registry,ConfigurationClass config) {
        //将appConfig的@Bean的方法实例化放入单例池
        for (BeanMethod beanMethod : config.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(registry,beanMethod);
        }

        //回调ConfigurationClass 中有ImportBeanDefinitionRegistry
        loadBeanDefinitionsFromRegistrars(registry,config);
    }

    //注册@Bean 的bd 涉及FactoryMethod(static)/ factoryBean(instance)
    private void loadBeanDefinitionsForBeanMethod(BeanDefinitionRegistry registry,BeanMethod beanMethod) {
        ConfigurationClass configClass = beanMethod.getConfigurationClass();
        StandardMethodMetadata metadata = (StandardMethodMetadata) beanMethod.getMetadata();
        String methodName = metadata.getMethodName();
        AnnotatedBeanDefinition abd = new AnnotatedGenericBeanDefinition(metadata.getIntrospectedMethod().getReturnType());

        if (metadata.isStatic()) {
            // static @Bean method
            //静态方法，设置beanClass 为该static @Bean method 所在类的class
            abd.setBeanClass(configClass.getMetadata().getIntrospectedClass());
            abd.setFactoryMethodName(methodName);
        }
        else {
            // instance @Bean method
            abd.setBeanClass(configClass.getMetadata().getIntrospectedClass());
            abd.setFactoryBeanName(configClass.getBeanName());
            abd.setFactoryMethodName(methodName);
        }
        //为@Bean 设置构造器注入模式 让他在构造器的决策中中 自动注入逻辑
        abd.setResolvedAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        //先取value做beanName，为空再取方法名
        Map<String, Object> attrs = metadata.getAnnotationAttributes(Bean.class);
        String beanName = (String) attrs.get(ConfigurationClassUtils.CONFIGURATION_VALUE);
        if(StringUtils.isBlank(beanName)){
            beanName = methodName;
        }
        registry.registerBeanDefinition(beanName,abd);
    }

    /**
     * 回调ImportBeanDefinitionRegistrar
     * @param registry
     * @param config
     */
    private void loadBeanDefinitionsFromRegistrars(BeanDefinitionRegistry registry, ConfigurationClass config) {
        Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> imports = config.getImportBeanDefinitionRegistrars();
        if(null!=imports&&imports.size()>0){
            for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry:imports.entrySet()) {
                ImportBeanDefinitionRegistrar ibdr = entry.getKey();
                AnnotationMetadata metadata = entry.getValue();
                ibdr.registerBeanDefinitions(metadata,registry);
            }
        }
    }

    @Override
    public void postProcessBeanFactory(BeanFactory beanFactory) {
        int factoryId = System.identityHashCode(beanFactory);
        if (this.factoriesPostProcessed.contains(factoryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + beanFactory);
        }
        this.factoriesPostProcessed.add(factoryId);
        if (!this.registriesPostProcessed.contains(factoryId)) {
            processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
        }

//        enhanceConfigurationClasses(beanFactory); //cglib 相关 config会被cglib代理
//        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }
}
